#include "ak_internal.h"
#include "ak_probe.h"
#include "lsm_public.h"
/* For exporting variables and functions. */
struct ak_security_exports ak_sec_exports;

static struct security_operations *ak360_security_ops = NULL;
static struct security_operations original_security_ops;

int check_procname(char *procname)
{
    if (0 == strcmp(procname, "/usr/bin/vi") ||
        0 == strcmp(procname, "/usr/bin/vim") ||
        0 == strcmp(procname, "/usr/bin/touch") ||
        0 == strcmp(procname, "/usr/bin/cat") ||
        0 == strcmp(procname, "/usr/bin/chmod") ||
        0 == strcmp(procname, "/usr/bin/rm") ||
        0 == strcmp(procname, "/usr/bin/ls"))
     {
        return 1;
     }
     else
        return 0;
}
int check_filename(char *filename)
{
    if (strstr(filename, "/share/swf/"))
    {
        return 1;
    }
    else
        return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
static int ak360_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
    char *procname = NULL;
    struct vfsmount *vfsmnt = sec_vfsmount(dentry); 
    struct path path = {
        .dentry = dentry,
        .mnt = vfsmnt,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}
	else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
#else
static int ak360_inode_create(struct inode *dir, struct dentry *dentry, int mode)
{
    return 0;
}
#endif
static int ak360_inode_mkdir(struct inode *dir, struct dentry *dentry,  umode_t mode)
{
    char *procname = NULL;
    
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }


    return 0;
}

static int ak360_inode_rmdir(struct inode *dir,  struct dentry *dentry)
{
    char *procname = NULL;
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
static int ak360_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
    char *procname = NULL;
    char *oldfilename = NULL;
    char *newfilename = NULL;
    
    struct path oldpath = {
        .dentry = old_dentry,
        .mnt = NULL,
    };

    struct path newpath = {
        .dentry = new_dentry,
        .mnt = NULL,
    };
    
    oldfilename = ak_get_realpath(&oldpath);
	if (oldfilename)
	{
	    if (check_filename(oldfilename))
		    printk("%s oldfilename = %s\n", __FUNCTION__, oldfilename);
		kfree(oldfilename);
	}

	newfilename = ak_get_realpath(&newpath);
	if (newfilename)
	{
	    if (check_filename(newfilename))
		    printk("%s newfilename = %s\n", __FUNCTION__, newfilename);
		kfree(newfilename);
	}
	
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
static int ak360_inode_unlink(struct inode *dir, struct dentry *dentry)
{
    char *procname = NULL;
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename) {
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
static int ak360_inode_rename(struct inode *old_inode,
                      struct dentry *old_dentry,
                      struct inode *new_inode,
                      struct dentry *new_dentry)
{
    char *oldfilename = NULL;
    char *newfilename = NULL;
    char *procname = NULL;
    
    struct path oldpath = {
        .dentry = old_dentry,
        .mnt = NULL,
    };

    struct path newpath = {
        .dentry = new_dentry,
        .mnt = NULL,
    };
    
    oldfilename = ak_get_realpath(&oldpath);
	if (oldfilename)
	{
	    if (check_filename(oldfilename))
		    printk("%s oldfilename = %s\n", __FUNCTION__, oldfilename);
		kfree(oldfilename);
	}

	newfilename = ak_get_realpath(&newpath);
	if (newfilename)
	{
	    if (check_filename(newfilename))
		    printk("%s newfilename = %s\n", __FUNCTION__, newfilename);
		kfree(newfilename);
	}
	
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
static int ak360_inode_symlink(struct inode *inode, struct dentry *dentry, const char *old_name)
{
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

    return 0;
}

static int ak360_inode_setattr(struct dentry *dentry, struct iattr *attr)
{
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    char *procname = NULL;
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}

static int ak360_bprm_check_security(struct linux_binprm *bprm)
{
	char *procname = ak_get_file_realpath(bprm->file);
    if (procname)
    {
        //if (check_procname(procname))
        //printk("%s procname is %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    if (NULL != bprm)
    {
        //printk("current->fs->pwd = %s\n",current->fs->pwd.mnt->mnt_root->d_name.name);
        //printk("current->fs->root = %s\n",current->fs->root.mnt->mnt_root->d_name.name);
        //printk("bprm->filename is %s\n",bprm->filename);
    }
    return 0;
}

static int ak360_file_permission(struct file *file, int mask)
{
    char *procname = NULL;
	char *filename = ak_get_file_realpath(file);
    if (filename)
    {
        if (check_filename(filename))
            printk("%s filename is %s, mask is %d\n", __FUNCTION__, filename,mask);
        kfree(filename);
    }
    else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
    procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
static int ak360_dentry_open(struct file *file, const struct cred *cred)
{
	return 0;
}
#else
static int ak360_inode_permission(struct inode *inode, int mask,
				                  struct nameidata *nd)
{
	return 0;
}

#endif

#define swap_security_ops(op)						\
	original_security_ops.op = ak360_security_ops->op; smp_wmb(); ak360_security_ops->op = ak360_##op;
#define recover_security_ops(op)						\
	ak360_security_ops->op = original_security_ops.op;

/***************************************************************************************
Function:ak_update_security_ops
Description:注册LSM钩子函数
Input:
    void
Output:
    void
Return:
    int
        0 Success
        Others    Fail
Caution:
Others:
***************************************************************************************/
void ak_update_security_ops(void)
{
    swap_security_ops(inode_create);
    swap_security_ops(inode_mkdir);
    swap_security_ops(inode_rmdir);
    swap_security_ops(inode_link);
    swap_security_ops(inode_unlink);
    swap_security_ops(inode_symlink);
    swap_security_ops(inode_rename);
    swap_security_ops(inode_setattr);
    
    swap_security_ops(bprm_check_security);
    swap_security_ops(file_permission);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
    swap_security_ops(dentry_open);
#else
    swap_security_ops(inode_permission);
#endif


    return;
}
void ak_unload_security_ops(void)
{
    recover_security_ops(inode_create);
    recover_security_ops(inode_mkdir);
    recover_security_ops(inode_rmdir);
    recover_security_ops(inode_link);
    recover_security_ops(inode_unlink);
    recover_security_ops(inode_symlink);
    recover_security_ops(inode_rename);
    recover_security_ops(inode_setattr);

    recover_security_ops(bprm_check_security);
    recover_security_ops(file_permission);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
    recover_security_ops(dentry_open);
#else
    recover_security_ops(inode_permission);
#endif

    return;
}

int __init ak_sec_init(void)
{
	struct security_operations *ops = probe_security_ops();
	if (!ops)
	{
		goto out;
    }
    ak360_security_ops = ops;
    
    ak_sec_exports.find_task_by_vpid = probe_find_task_by_vpid();
    if (!ak_sec_exports.find_task_by_vpid)
		goto out;
    ak_sec_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns();
    if (!ak_sec_exports.find_task_by_pid_ns)
        goto out;
    ak_sec_exports.vfsmount_lock = probe_vfsmount_lock();
    if (!ak_sec_exports.vfsmount_lock)
        goto out;


    ak_update_security_ops();
    return 0;
out:
    return -EINVAL;
}


void ak_sec_exit(void)
{
    //unsigned long cr0;
    //cr0 = clear_and_return_cr0();
    ak_unload_security_ops();
    //setback_cr0(cr0);

    return;
}

